use std::fmt;
-use std::io::{fs, BufReader, USER_RWX, File};
use std::io::fs::PathExtensions;
+use std::io::{fs, USER_RWX, File};
+use std::str;
use core::{Package, Target};
use util::{CargoResult, CargoError, human};
-use util::{internal, ChainError};
+use util::{internal, ChainError, Require};
use super::job::Work;
use super::{fingerprint, process, KindHost, Context};
/// Prepares a `Work` that executes the target as a custom build script.
pub fn prepare(pkg: &Package, target: &Target, cx: &mut Context)
-> CargoResult<(Work, Work, Freshness)> {
- let (script_output, build_output, old_build_output) = {
+ let (script_output, old_script_output, build_output, old_build_output) = {
let layout = cx.layout(pkg, KindHost);
(layout.build(pkg),
+ layout.proxy().old_build(pkg),
layout.build_out(pkg),
layout.proxy().old_build(pkg).join("out"))
};
let lib_name = pkg.get_manifest().get_links().map(|s| s.to_string());
let pkg_name = pkg.to_string();
let native_libs = cx.native_libs.clone();
+ let all = (lib_name.clone(), pkg_name.clone(), native_libs.clone(),
+ script_output.clone());
try!(fs::mkdir(&script_output, USER_RWX));
// This is also the location where we provide feedback into the build
// state informing what variables were discovered via our script as
// well.
- let rdr = BufReader::new(output.output.as_slice());
- let build_output = try!(BuildOutput::parse(rdr, pkg_name.as_slice()));
+ let output = try!(str::from_utf8(output.output.as_slice()).require(|| {
+ human("build script output was not valid utf-8")
+ }));
+ let build_output = try!(BuildOutput::parse(output, pkg_name.as_slice()));
match lib_name {
Some(name) => assert!(native_libs.lock().insert(name, build_output)),
None => {}
}
try!(File::create(&script_output.join("output"))
- .write(output.output.as_slice()).map_err(|e| {
+ .write_str(output).map_err(|e| {
human(format!("failed to write output of custom build command: {}",
e))
}));
// Note that the freshness calculation here is the build_cmd freshness, not
// target specific freshness. This is because we don't actually know what
// the inputs are to this command!
+ //
+ // Also note that a fresh build command needs to
let (freshness, dirty, fresh) =
try!(fingerprint::prepare_build_cmd(cx, pkg, Some(target)));
let dirty = proc(tx: Sender<String>) { try!(work(tx.clone())); dirty(tx) };
let fresh = proc(tx) {
+ let (lib_name, pkg_name, native_libs, script_output) = all;
+ let new_loc = script_output.join("output");
+ try!(fs::rename(&old_script_output.join("output"), &new_loc));
+ let mut f = try!(File::open(&new_loc).map_err(|e| {
+ human(format!("failed to read cached build command output: {}", e))
+ }));
+ let contents = try!(f.read_to_string());
+ let output = try!(BuildOutput::parse(contents.as_slice(),
+ pkg_name.as_slice()));
+ match lib_name {
+ Some(name) => assert!(native_libs.lock().insert(name, output)),
+ None => {}
+ }
+
fresh(tx)
};
impl BuildOutput {
// Parses the output of a script.
// The `pkg_name` is used for error messages.
- pub fn parse<B: Buffer>(mut input: B, pkg_name: &str) -> CargoResult<BuildOutput> {
+ pub fn parse(input: &str, pkg_name: &str) -> CargoResult<BuildOutput> {
let mut library_paths = Vec::new();
let mut library_links = Vec::new();
let mut metadata = Vec::new();
let whence = format!("build script of `{}`", pkg_name);
for line in input.lines() {
- // unwrapping the IoResult
- let line = try!(line.map_err(|e| human(format!("Error while reading\
- custom build output: {}", e))));
-
- let mut iter = line.as_slice().splitn(1, |c: char| c == ':');
+ let mut iter = line.splitn(1, |c: char| c == ':');
if iter.next() != Some("cargo") {
// skip this line since it doesn't start with "cargo:"
continue;
-use support::{project, execs};
-use support::{COMPILING, RUNNING};
+use std::io::File;
+
+use support::{project, execs, cargo_dir};
+use support::{COMPILING, RUNNING, FRESH};
+use support::paths::PathExt;
use hamcrest::{assert_that};
fn setup() {
", compiling = COMPILING, running = RUNNING).as_slice()));
})
+test!(only_rerun_build_script {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = []
+ build = "build.rs"
+ "#)
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {}
+ "#);
+
+ assert_that(p.cargo_process("build").arg("-v"),
+ execs().with_status(0));
+ p.root().move_into_the_past().unwrap();
+
+ File::create(&p.root().join("some-new-file")).unwrap();
+
+ assert_that(p.process(cargo_dir().join("cargo")).arg("build").arg("-v"),
+ execs().with_status(0)
+ .with_stdout(format!("\
+{compiling} foo v0.5.0 (file://[..])
+{running} `[..]build-script-build`
+{running} `rustc [..] --crate-name foo [..]`
+", compiling = COMPILING, running = RUNNING).as_slice()));
+})
+
+test!(rebuild_continues_to_pass_env_vars {
+ let a = project("a")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "a"
+ version = "0.5.0"
+ authors = []
+ links = "foo"
+ build = "build.rs"
+ "#)
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ println!("cargo:foo=bar");
+ println!("cargo:bar=baz");
+ }
+ "#);
+ a.build();
+ a.root().move_into_the_past().unwrap();
+
+ let p = project("foo")
+ .file("Cargo.toml", format!(r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = []
+ build = "build.rs"
+
+ [dependencies.a]
+ path = '{}'
+ "#, a.root().display()))
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ use std::os;
+ fn main() {
+ assert_eq!(os::getenv("DEP_FOO_FOO").unwrap().as_slice(), "bar");
+ assert_eq!(os::getenv("DEP_FOO_BAR").unwrap().as_slice(), "baz");
+ }
+ "#);
+
+ assert_that(p.cargo_process("build").arg("-v"),
+ execs().with_status(0));
+ p.root().move_into_the_past().unwrap();
+
+ File::create(&p.root().join("some-new-file")).unwrap();
+
+ assert_that(p.process(cargo_dir().join("cargo")).arg("build").arg("-v"),
+ execs().with_status(0)
+ .with_stdout(format!("\
+{fresh} a v0.5.0 (file://[..])
+{compiling} foo v0.5.0 (file://[..])
+{running} `[..]build-script-build`
+{running} `rustc [..] --crate-name foo [..]`
+", compiling = COMPILING, running = RUNNING, fresh = FRESH).as_slice()));
+})
+